/*
 * NOTICE OF RELEASE TO THE PUBLIC DOMAIN
 *
 * This work was created using public funds by employees of the 
 * USDA Forest Service's Fire Science Lab and Systems for Environmental 
 * Management.  It is therefore ineligible for copyright under title 17, 
 * section 105 of the United States Code.  You may treat it as you would 
 * treat any public domain work: it may be used, changed, copied, or 
 * redistributed, with or without permission of the authors, for free or 
 * for compensation.  You may not claim exclusive ownership of this code 
 * because it is already owned by everyone.  Use this software entirely 
 * at your own risk.  No warranty of any kind is given.
 * 
 * FARSITE is a trademark owned by Mark Finney.  You may not call derived 
 * works by the name FARSITE without explicit written permission.
 * 
 * A copy of 17-USC-105 should have accompanied this distribution in the file 
 * 17USC105.html.  If not, you may access the law via the US Government's 
 * public websites: 
 *   - http://www.copyright.gov/title17/92chap1.html#105
 *   - http://www.gpoaccess.gov/uscode/  (enter "17USC105" in the search box.)
 */
/******************************************************************************
 * $Id: shpopen.c,v 1.33 2001/07/03 12:18:15 warmerda Exp $
 *
 * Project:  Shapelib
 * Purpose:  Implementation of core Shapefile read/write functions.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 1999, 2001, Frank Warmerdam
 *
 * This software is available under the following "MIT Style" license,
 * or at the option of the licensee under the LGPL (see LICENSE.LGPL).  This
 * option is discussed in more detail in shapelib.html.
 *
 * --
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ******************************************************************************
 *                                
 * $Log: shpopen.c,v $
 * Revision 1.33  2001/07/03 12:18:15  warmerda
 * Improved cleanup if SHX not found, provied by Riccardo Cohen.
 *
 * Revision 1.32  2001/06/22 01:58:07  warmerda
 * be more careful about establishing initial bounds in face of NULL shapes
 *
 * Revision 1.31  2001/05/31 19:35:29  warmerda
 * added support for writing null shapes
 *
 * Revision 1.30  2001/05/28 12:46:29  warmerda
 * Add some checking on reasonableness of record count when opening.
 *
 * Revision 1.29  2001/05/23 13:36:52  warmerda
 * added use of SHPAPI_CALL
 *
 * Revision 1.28  2001/02/06 22:25:06  warmerda
 * fixed memory leaks when SHPOpen() fails
 *
 * Revision 1.27  2000/07/18 15:21:33  warmerda
 * added better enforcement of -1 for append in SHPWriteObject
 *
 * Revision 1.26  2000/02/16 16:03:51  warmerda
 * added null shape support
 *
 * Revision 1.25  1999/12/15 13:47:07  warmerda
 * Fixed record size settings in .shp file (was 4 words too long)
 * Added stdlib.h.
 *
 * Revision 1.24  1999/11/05 14:12:04  warmerda
 * updated license terms
 *
 * Revision 1.23  1999/07/27 00:53:46  warmerda
 * added support for rewriting shapes
 *
 * Revision 1.22  1999/06/11 19:19:11  warmerda
 * Cleanup pabyRec static buffer on SHPClose().
 *
 * Revision 1.21  1999/06/02 14:57:56  kshih
 * Remove unused variables
 *
 * Revision 1.20  1999/04/19 21:04:17  warmerda
 * Fixed syntax error.
 *
 * Revision 1.19  1999/04/19 21:01:57  warmerda
 * Force access string to binary in SHPOpen().
 *
 * Revision 1.18  1999/04/01 18:48:07  warmerda
 * Try upper case extensions if lower case doesn't work.
 *
 * Revision 1.17  1998/12/31 15:29:39  warmerda
 * Disable writing measure values to multipatch objects if
 * DISABLE_MULTIPATCH_MEASURE is defined.
 *
 * Revision 1.16  1998/12/16 05:14:33  warmerda
 * Added support to write MULTIPATCH.  Fixed reading Z coordinate of
 * MULTIPATCH. Fixed record size written for all feature types.
 *
 * Revision 1.15  1998/12/03 16:35:29  warmerda
 * r+b is proper binary access string, not rb+.
 *
 * Revision 1.14  1998/12/03 15:47:56  warmerda
 * Fixed setting of nVertices in SHPCreateObject().
 *
 * Revision 1.13  1998/12/03 15:33:54  warmerda
 * Made SHPCalculateExtents() separately callable.
 *
 * Revision 1.12  1998/11/11 20:01:50  warmerda
 * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines.
 *
 * Revision 1.11  1998/11/09 20:56:44  warmerda
 * Fixed up handling of file wide bounds.
 *
 * Revision 1.10  1998/11/09 20:18:51  warmerda
 * Converted to support 3D shapefiles, and use of SHPObject.
 *
 * Revision 1.9  1998/02/24 15:09:05  warmerda
 * Fixed memory leak.
 *
 * Revision 1.8  1997/12/04 15:40:29  warmerda
 * Fixed byte swapping of record number, and record length fields in the
 * .shp file.
 *
 * Revision 1.7  1995/10/21 03:15:58  warmerda
 * Added support for binary file access, the magic cookie 9997
 * and tried to improve the int32 selection logic for 16bit systems.
 *
 * Revision 1.6  1995/09/04  04:19:41  warmerda
 * Added fix for file bounds.
 *
 * Revision 1.5  1995/08/25  15:16:44  warmerda
 * Fixed a couple of problems with big endian systems ... one with bounds
 * and the other with multipart polygons.
 *
 * Revision 1.4  1995/08/24  18:10:17  warmerda
 * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc()
 * functions (such as on the Sun).
 *
 * Revision 1.3  1995/08/23  02:23:15  warmerda
 * Added support for reading bounds, and fixed up problems in setting the
 * file wide bounds.
 *
 * Revision 1.2  1995/08/04  03:16:57  warmerda
 * Added header.
 *
 */

//static char rcsid[] = "$Id: shpopen.c,v 1.33 2001/07/03 12:18:15 warmerda Exp $";

#include "shapefil.h"

#include <math.h>
#include <limits.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char uchar;

#if UINT_MAX == 65535
typedef long int32;
#else
typedef int int32;
#endif

#ifndef FALSE
#  define FALSE		0
#  define TRUE		1
#endif

#define ByteCopy( a, b, c )	memcpy( b, a, c )
#ifndef MAX
#  define MIN(a,b)  	((a<b) ? a : b)
#  define MAX(a,b)  	((a>b) ? a : b)
#endif

static int bBigEndian;
static uchar* pabyRec = NULL;
static int nBufSize = 0;


/************************************************************************/
/*  							SwapWord()  							*/
/*  																	*/
/*  	Swap a 2, 4 or 8 byte word. 									*/
/************************************************************************/

static void SwapWord(int length, void* wordP)
{
	int i;
	uchar temp;

	for (i = 0; i < length / 2; i++)
	{
		temp = ((uchar *) wordP)[i];
		((uchar *) wordP)[i] = ((uchar *) wordP)[length - i - 1];
		((uchar *) wordP)[length - i - 1] = temp;
	}
}

/************************************************************************/
/*  						   SfRealloc()  							*/
/*  																	*/
/*  	A realloc cover function that will access a NULL pointer as 	*/
/*  	a valid input.  												*/
/************************************************************************/

static void* SfRealloc(void* pMem, int nNewSize)
{
	if (pMem == NULL)
		return((void *) malloc(nNewSize));
	else
		return((void *) realloc(pMem, nNewSize));
}

/************************************************************************/
/*  						SHPWriteHeader()							*/
/*  																	*/
/*  	Write out a header for the .shp and .shx files as well as the	*/
/*	contents of the index (.shx) file.				*/
/************************************************************************/

static void SHPWriteHeader(SHPHandle psSHP)
{
	uchar abyHeader[100];
	int i;
	int32 i32;
	double dValue;
	int32* panSHX;

	/* -------------------------------------------------------------------- */
	/*  	Prepare header block for .shp file. 							*/
	/* -------------------------------------------------------------------- */
	for (i = 0; i < 100; i++)
		abyHeader[i] = 0;

	abyHeader[2] = 0x27;				/* magic cookie */
	abyHeader[3] = 0x0a;

	i32 = psSHP->nFileSize / 2;				/* file size */
	ByteCopy(&i32, abyHeader + 24, 4);
	if (!bBigEndian)
		SwapWord(4, abyHeader + 24);

	i32 = 1000;						/* version */
	ByteCopy(&i32, abyHeader + 28, 4);
	if (bBigEndian)
		SwapWord(4, abyHeader + 28);

	i32 = psSHP->nShapeType;				/* shape type */
	ByteCopy(&i32, abyHeader + 32, 4);
	if (bBigEndian)
		SwapWord(4, abyHeader + 32);

	dValue = psSHP->adBoundsMin[0];			/* set bounds */
	ByteCopy(&dValue, abyHeader + 36, 8);
	if (bBigEndian)
		SwapWord(8, abyHeader + 36);

	dValue = psSHP->adBoundsMin[1];
	ByteCopy(&dValue, abyHeader + 44, 8);
	if (bBigEndian)
		SwapWord(8, abyHeader + 44);

	dValue = psSHP->adBoundsMax[0];
	ByteCopy(&dValue, abyHeader + 52, 8);
	if (bBigEndian)
		SwapWord(8, abyHeader + 52);

	dValue = psSHP->adBoundsMax[1];
	ByteCopy(&dValue, abyHeader + 60, 8);
	if (bBigEndian)
		SwapWord(8, abyHeader + 60);

	dValue = psSHP->adBoundsMin[2];			/* z */
	ByteCopy(&dValue, abyHeader + 68, 8);
	if (bBigEndian)
		SwapWord(8, abyHeader + 68);

	dValue = psSHP->adBoundsMax[2];
	ByteCopy(&dValue, abyHeader + 76, 8);
	if (bBigEndian)
		SwapWord(8, abyHeader + 76);

	dValue = psSHP->adBoundsMin[3];			/* m */
	ByteCopy(&dValue, abyHeader + 84, 8);
	if (bBigEndian)
		SwapWord(8, abyHeader + 84);

	dValue = psSHP->adBoundsMax[3];
	ByteCopy(&dValue, abyHeader + 92, 8);
	if (bBigEndian)
		SwapWord(8, abyHeader + 92);

	/* -------------------------------------------------------------------- */
	/*  	Write .shp file header. 										*/
	/* -------------------------------------------------------------------- */
	fseek(psSHP->fpSHP, 0, 0);
	fwrite(abyHeader, 100, 1, psSHP->fpSHP);

	/* -------------------------------------------------------------------- */
	/*  	Prepare, and write .shx file header.							*/
	/* -------------------------------------------------------------------- */
	i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100) / 2;   /* file size */
	ByteCopy(&i32, abyHeader + 24, 4);
	if (!bBigEndian)
		SwapWord(4, abyHeader + 24);

	fseek(psSHP->fpSHX, 0, 0);
	fwrite(abyHeader, 100, 1, psSHP->fpSHX);

	/* -------------------------------------------------------------------- */
	/*  	Write out the .shx contents.									*/
	/* -------------------------------------------------------------------- */
	panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords);

	for (i = 0; i < psSHP->nRecords; i++)
	{
		panSHX[i * 2] = psSHP->panRecOffset[i] / 2;
		panSHX[i * 2 + 1] = psSHP->panRecSize[i] / 2;
		if (!bBigEndian)
			SwapWord(4, panSHX + i * 2);
		if (!bBigEndian)
			SwapWord(4, panSHX + i * 2 + 1);
	}

	fwrite(panSHX, sizeof(int32) * 2, psSHP->nRecords, psSHP->fpSHX);

	free(panSHX);
}

/************************************************************************/
/*  							SHPOpen()   							*/
/*  																	*/
/*  	Open the .shp and .shx files based on the basename of the   	*/
/*  	files or either file name.  									*/
/************************************************************************/

SHPHandle SHPAPI_CALL SHPOpen(const char* pszLayer, const char* pszAccess)
{
	char* pszFullname, * pszBasename;
	SHPHandle psSHP;

	uchar* pabyBuf;
	int i;
	double dValue;

	/* -------------------------------------------------------------------- */
	/*  	Ensure the access string is one of the legal ones.  We  		*/
	/*  	ensure the result string indicates binary to avoid common   	*/
	/*  	problems on Windows.											*/
	/* -------------------------------------------------------------------- */
	if (strcmp(pszAccess, "rb+") ==
		0 ||
		strcmp(pszAccess, "r+b") ==
		0 ||
		strcmp(pszAccess,
			"r+") ==
		0)
		pszAccess = "r+b";
	else
		pszAccess = "rb";

	/* -------------------------------------------------------------------- */
	/*	Establish the byte order on this machine.			*/
	/* -------------------------------------------------------------------- */
	i = 1;
	if (*((uchar *) &i) == 1)
		bBigEndian = FALSE;
	else
		bBigEndian = TRUE;

	/* -------------------------------------------------------------------- */
	/*	Initialize the info structure.					*/
	/* -------------------------------------------------------------------- */
	psSHP = (SHPHandle) malloc(sizeof(SHPInfo));

	psSHP->bUpdated = FALSE;

	/* -------------------------------------------------------------------- */
	/*	Compute the base (layer) name.  If there is any extension	*/
	/*	on the passed in filename we will strip it off.			*/
	/* -------------------------------------------------------------------- */
	pszBasename = (char *) malloc(strlen(pszLayer) + 5);
	strcpy(pszBasename, pszLayer);
	for (i = strlen(pszBasename) - 1;
		i > 0 &&
		pszBasename[i] != '.' &&
		pszBasename[i] != '/' &&
		pszBasename[i] != '\\';
		i--)
	{
	}

	if (pszBasename[i] == '.')
		pszBasename[i] = '\0';

	/* -------------------------------------------------------------------- */
	/*	Open the .shp and .shx files.  Note that files pulled from	*/
	/*	a PC to Unix with upper case filenames won't work!		*/
	/* -------------------------------------------------------------------- */
	pszFullname = (char *) malloc(strlen(pszBasename) + 5);
	sprintf(pszFullname, "%s.shp", pszBasename);
	psSHP->fpSHP = fopen(pszFullname, pszAccess);
	if (psSHP->fpSHP == NULL)
	{
		sprintf(pszFullname, "%s.SHP", pszBasename);
		psSHP->fpSHP = fopen(pszFullname, pszAccess);
	}

	if (psSHP->fpSHP == NULL)
	{
		free(psSHP);
		free(pszBasename);
		free(pszFullname);
		return(NULL);
	}

	sprintf(pszFullname, "%s.shx", pszBasename);
	psSHP->fpSHX = fopen(pszFullname, pszAccess);
	if (psSHP->fpSHX == NULL)
	{
		sprintf(pszFullname, "%s.SHX", pszBasename);
		psSHP->fpSHX = fopen(pszFullname, pszAccess);
	}

	if (psSHP->fpSHX == NULL)
	{
		fclose(psSHP->fpSHX);
		free(psSHP);
		free(pszBasename);
		free(pszFullname);
		return(NULL);
	}

	free(pszFullname);
	free(pszBasename);

	/* -------------------------------------------------------------------- */
	/*  Read the file size from the SHP file.				*/
	/* -------------------------------------------------------------------- */
	pabyBuf = (uchar *) malloc(100);
	fread(pabyBuf, 100, 1, psSHP->fpSHP);

	psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256 +
		pabyBuf[25] * 256 * 256 +
		pabyBuf[26] * 256 +
		pabyBuf[27]) * 2;

	/* -------------------------------------------------------------------- */
	/*  Read SHX file Header info   										*/
	/* -------------------------------------------------------------------- */
	fread(pabyBuf, 100, 1, psSHP->fpSHX);

	if (pabyBuf[0] != 0 ||
		pabyBuf[1] != 0 ||
		pabyBuf[2] != 0x27 ||
		(pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d))
	{
		fclose(psSHP->fpSHP);
		fclose(psSHP->fpSHX);
		free(psSHP);

		return(NULL);
	}

	psSHP->nRecords = pabyBuf[27] +
		pabyBuf[26] * 256 +
		pabyBuf[25] * 256 * 256 +
		pabyBuf[24] * 256 * 256 * 256;
	psSHP->nRecords = (psSHP->nRecords * 2 - 100) / 8;

	psSHP->nShapeType = pabyBuf[32];

	if (psSHP->nRecords <0 || psSHP->nRecords> 256000000)
	{
		/* this header appears to be corrupt.  Give up. */
		fclose(psSHP->fpSHP);
		fclose(psSHP->fpSHX);
		free(psSHP);

		return(NULL);
	}

	/* -------------------------------------------------------------------- */
	/*  	Read the bounds.												*/
	/* -------------------------------------------------------------------- */
	if (bBigEndian)
		SwapWord(8, pabyBuf + 36);
	memcpy(&dValue, pabyBuf + 36, 8);
	psSHP->adBoundsMin[0] = dValue;

	if (bBigEndian)
		SwapWord(8, pabyBuf + 44);
	memcpy(&dValue, pabyBuf + 44, 8);
	psSHP->adBoundsMin[1] = dValue;

	if (bBigEndian)
		SwapWord(8, pabyBuf + 52);
	memcpy(&dValue, pabyBuf + 52, 8);
	psSHP->adBoundsMax[0] = dValue;

	if (bBigEndian)
		SwapWord(8, pabyBuf + 60);
	memcpy(&dValue, pabyBuf + 60, 8);
	psSHP->adBoundsMax[1] = dValue;

	if (bBigEndian)
		SwapWord(8, pabyBuf + 68);		/* z */
	memcpy(&dValue, pabyBuf + 68, 8);
	psSHP->adBoundsMin[2] = dValue;

	if (bBigEndian)
		SwapWord(8, pabyBuf + 76);
	memcpy(&dValue, pabyBuf + 76, 8);
	psSHP->adBoundsMax[2] = dValue;

	if (bBigEndian)
		SwapWord(8, pabyBuf + 84);		/* z */
	memcpy(&dValue, pabyBuf + 84, 8);
	psSHP->adBoundsMin[3] = dValue;

	if (bBigEndian)
		SwapWord(8, pabyBuf + 92);
	memcpy(&dValue, pabyBuf + 92, 8);
	psSHP->adBoundsMax[3] = dValue;

	free(pabyBuf);

	/* -------------------------------------------------------------------- */
	/*	Read the .shx file to get the offsets to each record in 	*/
	/*	the .shp file.							*/
	/* -------------------------------------------------------------------- */
	psSHP->nMaxRecords = psSHP->nRecords;

	psSHP->panRecOffset = (int *)
		malloc(sizeof(int) * MAX(1, psSHP->nMaxRecords));
	psSHP->panRecSize = (int *)
		malloc(sizeof(int) * MAX(1, psSHP->nMaxRecords));

	pabyBuf = (uchar *) malloc(8 * MAX(1, psSHP->nRecords));
	fread(pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX);

	for (i = 0; i < psSHP->nRecords; i++)
	{
		int32 nOffset, nLength;

		memcpy(&nOffset, pabyBuf + i * 8, 4);
		if (!bBigEndian)
			SwapWord(4, &nOffset);

		memcpy(&nLength, pabyBuf + i * 8 + 4, 4);
		if (!bBigEndian)
			SwapWord(4, &nLength);

		psSHP->panRecOffset[i] = nOffset * 2;
		psSHP->panRecSize[i] = nLength * 2;
	}
	free(pabyBuf);

	return(psSHP);
}

/************************************************************************/
/*  							SHPClose()  							*/
/*									   	*/
/*	Close the .shp and .shx files.					*/
/************************************************************************/

void SHPAPI_CALL SHPClose(SHPHandle psSHP)
{
	/* -------------------------------------------------------------------- */
	/*	Update the header if we have modified anything.			*/
	/* -------------------------------------------------------------------- */
	if (psSHP->bUpdated)
	{
		SHPWriteHeader(psSHP);
	}

	/* -------------------------------------------------------------------- */
	/*  	Free all resources, and close files.							*/
	/* -------------------------------------------------------------------- */
	free(psSHP->panRecOffset);
	free(psSHP->panRecSize);

	fclose(psSHP->fpSHX);
	fclose(psSHP->fpSHP);

	free(psSHP);

	if (pabyRec != NULL)
	{
		free(pabyRec);
		pabyRec = NULL;
		nBufSize = 0;
	}
}

/************************************************************************/
/*  						   SHPGetInfo() 							*/
/*  																	*/
/*  	Fetch general information about the shape file. 				*/
/************************************************************************/

void SHPAPI_CALL SHPGetInfo(SHPHandle psSHP, int* pnEntities,
	int* pnShapeType, double* padfMinBound, double* padfMaxBound)
{
	int i;

	if (pnEntities != NULL)
		*pnEntities = psSHP->nRecords;

	if (pnShapeType != NULL)
		*pnShapeType = psSHP->nShapeType;

	for (i = 0; i < 4; i++)
	{
		if (padfMinBound != NULL)
			padfMinBound[i] = psSHP->adBoundsMin[i];
		if (padfMaxBound != NULL)
			padfMaxBound[i] = psSHP->adBoundsMax[i];
	}
}

/************************************************************************/
/*  						   SHPCreate()  							*/
/*  																	*/
/*  	Create a new shape file and return a handle to the open 		*/
/*  	shape file with read/write access.  							*/
/************************************************************************/

SHPHandle SHPAPI_CALL SHPCreate(const char* pszLayer, int nShapeType)
{
	char* pszBasename, * pszFullname;
	int i;
	FILE* fpSHP, * fpSHX;
	uchar abyHeader[100];
	int32 i32;
	double dValue;

	/* -------------------------------------------------------------------- */
	/*  	Establish the byte order on this system.						*/
	/* -------------------------------------------------------------------- */
	i = 1;
	if (*((uchar *) &i) == 1)
		bBigEndian = FALSE;
	else
		bBigEndian = TRUE;

	/* -------------------------------------------------------------------- */
	/*	Compute the base (layer) name.  If there is any extension	*/
	/*	on the passed in filename we will strip it off.			*/
	/* -------------------------------------------------------------------- */
	pszBasename = (char *) malloc(strlen(pszLayer) + 5);
	strcpy(pszBasename, pszLayer);
	for (i = strlen(pszBasename) - 1;
		i > 0 &&
		pszBasename[i] != '.' &&
		pszBasename[i] != '/' &&
		pszBasename[i] != '\\';
		i--)
	{
	}

	if (pszBasename[i] == '.')
		pszBasename[i] = '\0';

	/* -------------------------------------------------------------------- */
	/*  	Open the two files so we can write their headers.   			*/
	/* -------------------------------------------------------------------- */
	pszFullname = (char *) malloc(strlen(pszBasename) + 5);
	sprintf(pszFullname, "%s.shp", pszBasename);
	fpSHP = fopen(pszFullname, "wb");
	if (fpSHP == NULL)
		return(NULL);

	sprintf(pszFullname, "%s.shx", pszBasename);
	fpSHX = fopen(pszFullname, "wb");
	if (fpSHX == NULL)
		return(NULL);

	free(pszFullname);
	free(pszBasename);

	/* -------------------------------------------------------------------- */
	/*  	Prepare header block for .shp file. 							*/
	/* -------------------------------------------------------------------- */
	for (i = 0; i < 100; i++)
		abyHeader[i] = 0;

	abyHeader[2] = 0x27;				/* magic cookie */
	abyHeader[3] = 0x0a;

	i32 = 50;						/* file size */
	ByteCopy(&i32, abyHeader + 24, 4);
	if (!bBigEndian)
		SwapWord(4, abyHeader + 24);

	i32 = 1000;						/* version */
	ByteCopy(&i32, abyHeader + 28, 4);
	if (bBigEndian)
		SwapWord(4, abyHeader + 28);

	i32 = nShapeType;					/* shape type */
	ByteCopy(&i32, abyHeader + 32, 4);
	if (bBigEndian)
		SwapWord(4, abyHeader + 32);

	dValue = 0.0;					/* set bounds */
	ByteCopy(&dValue, abyHeader + 36, 8);
	ByteCopy(&dValue, abyHeader + 44, 8);
	ByteCopy(&dValue, abyHeader + 52, 8);
	ByteCopy(&dValue, abyHeader + 60, 8);

	/* -------------------------------------------------------------------- */
	/*  	Write .shp file header. 										*/
	/* -------------------------------------------------------------------- */
	fwrite(abyHeader, 100, 1, fpSHP);

	/* -------------------------------------------------------------------- */
	/*  	Prepare, and write .shx file header.							*/
	/* -------------------------------------------------------------------- */
	i32 = 50;						/* file size */
	ByteCopy(&i32, abyHeader + 24, 4);
	if (!bBigEndian)
		SwapWord(4, abyHeader + 24);

	fwrite(abyHeader, 100, 1, fpSHX);

	/* -------------------------------------------------------------------- */
	/*  	Close the files, and then open them as regular existing files.  */
	/* -------------------------------------------------------------------- */
	fclose(fpSHP);
	fclose(fpSHX);

	return(SHPOpen(pszLayer, "r+b"));
}

/************************************************************************/
/*  						 _SHPSetBounds()							*/
/*  																	*/
/*  	Compute a bounds rectangle for a shape, and set it into the 	*/
/*  	indicated location in the record.   							*/
/************************************************************************/

static void _SHPSetBounds(uchar* pabyRec, SHPObject* psShape)
{
	ByteCopy(&(psShape->dfXMin), pabyRec + 0, 8);
	ByteCopy(&(psShape->dfYMin), pabyRec + 8, 8);
	ByteCopy(&(psShape->dfXMax), pabyRec + 16, 8);
	ByteCopy(&(psShape->dfYMax), pabyRec + 24, 8);

	if (bBigEndian)
	{
		SwapWord(8, pabyRec + 0);
		SwapWord(8, pabyRec + 8);
		SwapWord(8, pabyRec + 16);
		SwapWord(8, pabyRec + 24);
	}
}

/************************************************************************/
/*  					   SHPComputeExtents()  						*/
/*  																	*/
/*  	Recompute the extents of a shape.  Automatically done by		*/
/*  	SHPCreateObject().  											*/
/************************************************************************/

void SHPAPI_CALL SHPComputeExtents(SHPObject* psObject)
{
	int i;

	/* -------------------------------------------------------------------- */
	/*  	Build extents for this object.  								*/
	/* -------------------------------------------------------------------- */
	if (psObject->nVertices > 0)
	{
		psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
		psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
		psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
		psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
	}

	for (i = 0; i < psObject->nVertices; i++)
	{
		psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
		psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
		psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
		psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);

		psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
		psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
		psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
		psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
	}
}

/************************************************************************/
/*  						SHPCreateObject()   						*/
/*  																	*/
/*  	Create a shape object.  It should be freed with 				*/
/*  	SHPDestroyObject(). 											*/
/************************************************************************/

SHPObject SHPAPI_CALL1(*)
SHPCreateObject(int nSHPType, int nShapeId, int nParts, int* panPartStart,
	int* panPartType, int nVertices, double* padfX, double* padfY,
	double* padfZ, double* padfM)

{
SHPObject	*psObject;
int i, bHasM, bHasZ;

	psObject = (SHPObject*) calloc(1, sizeof(SHPObject));
psObject-> nSHPType = nSHPType;
psObject-> nShapeId = nShapeId;

/* -------------------------------------------------------------------- */
/*	Establish whether this shape type has M, and Z values.		*/
/* -------------------------------------------------------------------- */
if (nSHPType == SHPT_ARCM ||
	nSHPType == SHPT_POINTM ||
	nSHPType == SHPT_POLYGONM ||
	nSHPType == SHPT_MULTIPOINTM)
{
	bHasM = TRUE;
	bHasZ = FALSE;
}
else if (nSHPType == SHPT_ARCZ ||
	nSHPType == SHPT_POINTZ ||
	nSHPType == SHPT_POLYGONZ ||
	nSHPType == SHPT_MULTIPOINTZ ||
	nSHPType == SHPT_MULTIPATCH)
{
	bHasM = TRUE;
	bHasZ = TRUE;
}
else
{
	bHasM = FALSE;
	bHasZ = FALSE;
}

/* -------------------------------------------------------------------- */
/*  	Capture parts.  Note that part type is optional, and			*/
/*  	defaults to ring.   											*/
/* -------------------------------------------------------------------- */
if (nSHPType == SHPT_ARC ||
	nSHPType == SHPT_POLYGON ||
	nSHPType == SHPT_ARCM ||
	nSHPType == SHPT_POLYGONM ||
	nSHPType == SHPT_ARCZ ||
	nSHPType == SHPT_POLYGONZ ||
	nSHPType == SHPT_MULTIPATCH)
{
	psObject->nParts = MAX(1, nParts);

	psObject->panPartStart = (int *) malloc(sizeof(int) * psObject->nParts);
	psObject->panPartType = (int *) malloc(sizeof(int) * psObject->nParts);

	psObject->panPartStart[0] = 0;
	psObject->panPartType[0] = SHPP_RING;

	for (i = 0; i < nParts; i++)
	{
		psObject->panPartStart[i] = panPartStart[i];
		if (panPartType != NULL)
			psObject->panPartType[i] = panPartType[i];
		else
			psObject->panPartType[i] = SHPP_RING;
	}
}

/* -------------------------------------------------------------------- */
/*  	Capture vertices.  Note that Z and M are optional, but X and	*/
/*  	Y are not.  													*/
/* -------------------------------------------------------------------- */
if (nVertices > 0)
{
	psObject->padfX = (double *) calloc(sizeof(double), nVertices);
	psObject->padfY = (double *) calloc(sizeof(double), nVertices);
	psObject->padfZ = (double *) calloc(sizeof(double), nVertices);
	psObject->padfM = (double *) calloc(sizeof(double), nVertices);

	assert(padfX != NULL);
	assert(padfY != NULL);

	for (i = 0; i < nVertices; i++)
	{
		psObject->padfX[i] = padfX[i];
		psObject->padfY[i] = padfY[i];
		if (padfZ != NULL && bHasZ)
			psObject->padfZ[i] = padfZ[i];
		if (padfM != NULL && bHasM)
			psObject->padfM[i] = padfM[i];
	}
}

/* -------------------------------------------------------------------- */
/*  	Compute the extents.											*/
/* -------------------------------------------------------------------- */
psObject-> nVertices = nVertices;
SHPComputeExtents(psObject);

return(psObject);
}

/************************************************************************/
/*  					 SHPCreateSimpleObject()						*/
/*  																	*/
/*  	Create a simple (common) shape object.  Destroy with			*/
/*  	SHPDestroyObject(). 											*/
/************************************************************************/

SHPObject SHPAPI_CALL1(*)
SHPCreateSimpleObject(int nSHPType, int nVertices, double* padfX,
	double* padfY, double* padfZ)

{
return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL,
nVertices, padfX, padfY, padfZ, NULL ) );
}

/************************************************************************/
/*  						 SHPWriteObject()   						*/
/*  																	*/
/*  	Write out the vertices of a new structure.  Note that it is 	*/
/*  	only possible to write vertices at the end of the file. 		*/
/************************************************************************/

int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId,
	SHPObject* psObject)
{
	int nRecordOffset, i, nRecordSize;
	uchar* pabyRec;
	int32 i32;

	psSHP->bUpdated = TRUE;

	/* -------------------------------------------------------------------- */
	/*  	Ensure that shape object matches the type of the file it is 	*/
	/*  	being written to.   											*/
	/* -------------------------------------------------------------------- */
	assert(psObject->nSHPType == psSHP->nShapeType ||
		psObject->nSHPType == SHPT_NULL);

	/* -------------------------------------------------------------------- */
	/*  	Ensure that -1 is used for appends.  Either blow an 			*/
	/*  	assertion, or if they are disabled, set the shapeid to -1   	*/
	/*  	for appends.													*/
	/* -------------------------------------------------------------------- */
	assert(nShapeId == -1 || (nShapeId >= 0 && nShapeId < psSHP->nRecords));

	if (nShapeId != -1 && nShapeId >= psSHP->nRecords)
		nShapeId = -1;

	/* -------------------------------------------------------------------- */
	/*  	Add the new entity to the in memory index.  					*/
	/* -------------------------------------------------------------------- */
	if (nShapeId == -1 && psSHP->nRecords + 1 > psSHP->nMaxRecords)
	{
		psSHP->nMaxRecords = (int) (psSHP->nMaxRecords * 1.3 + 100);

		psSHP->panRecOffset = (int *) SfRealloc(psSHP->panRecOffset,
										sizeof(int) * psSHP->nMaxRecords);
		psSHP->panRecSize = (int *) SfRealloc(psSHP->panRecSize,
										sizeof(int) * psSHP->nMaxRecords);
	}

	/* -------------------------------------------------------------------- */
	/*  	Initialize record.  											*/
	/* -------------------------------------------------------------------- */
	pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) +
							psObject->nParts * 8 +
							128);

	/* -------------------------------------------------------------------- */
	/*  Extract vertices for a Polygon or Arc.				*/
	/* -------------------------------------------------------------------- */
	if (psObject->nSHPType == SHPT_POLYGON ||
		psObject->nSHPType == SHPT_POLYGONZ ||
		psObject->nSHPType == SHPT_POLYGONM ||
		psObject->nSHPType == SHPT_ARC ||
		psObject->nSHPType == SHPT_ARCZ ||
		psObject->nSHPType == SHPT_ARCM ||
		psObject->nSHPType == SHPT_MULTIPATCH)
	{
		int32 nPoints, nParts;
		int i;

		nPoints = psObject->nVertices;
		nParts = psObject->nParts;

		_SHPSetBounds(pabyRec + 12, psObject);

		if (bBigEndian)
			SwapWord(4, &nPoints);
		if (bBigEndian)
			SwapWord(4, &nParts);

		ByteCopy(&nPoints, pabyRec + 40 + 8, 4);
		ByteCopy(&nParts, pabyRec + 36 + 8, 4);

		nRecordSize = 52;

		/*
			 * Write part start positions.
			 */
		ByteCopy(psObject->panPartStart, pabyRec + 44 + 8,
			4 * psObject->nParts);
		for (i = 0; i < psObject->nParts; i++)
		{
			if (bBigEndian)
				SwapWord(4, pabyRec + 44 + 8 + 4 * i);
			nRecordSize += 4;
		}

		/*
			 * Write multipatch part types if needed.
			 */
		if (psObject->nSHPType == SHPT_MULTIPATCH)
		{
			memcpy(pabyRec + nRecordSize, psObject->panPartType,
				4 * psObject->nParts);
			for (i = 0; i < psObject->nParts; i++)
			{
				if (bBigEndian)
					SwapWord(4, pabyRec + nRecordSize);
				nRecordSize += 4;
			}
		}

		/*
			 * Write the (x,y) vertex values.
			 */
		for (i = 0; i < psObject->nVertices; i++)
		{
			ByteCopy(psObject->padfX + i, pabyRec + nRecordSize, 8);
			ByteCopy(psObject->padfY + i, pabyRec + nRecordSize + 8, 8);

			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);

			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize + 8);

			nRecordSize += 2 * 8;
		}

		/*
			 * Write the Z coordinates (if any).
			 */
		if (psObject->nSHPType == SHPT_POLYGONZ ||
			psObject->nSHPType == SHPT_ARCZ ||
			psObject->nSHPType == SHPT_MULTIPATCH)
		{
			ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;

			ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;

			for (i = 0; i < psObject->nVertices; i++)
			{
				ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8);
				if (bBigEndian)
					SwapWord(8, pabyRec + nRecordSize);
				nRecordSize += 8;
			}
		}

		/*
			 * Write the M values, if any.
			 */
		if (psObject->nSHPType == SHPT_POLYGONM ||
			psObject->nSHPType == SHPT_ARCM
#ifndef DISABLE_MULTIPATCH_MEASURE  		  
			||
			psObject->nSHPType == SHPT_MULTIPATCH
#endif  		  
			||
			psObject->nSHPType == SHPT_POLYGONZ ||
			psObject->nSHPType == SHPT_ARCZ)
		{
			ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;

			ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;

			for (i = 0; i < psObject->nVertices; i++)
			{
				ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8);
				if (bBigEndian)
					SwapWord(8, pabyRec + nRecordSize);
				nRecordSize += 8;
			}
		}
	}

	/* -------------------------------------------------------------------- */
	/*  Extract vertices for a MultiPoint.					*/
	/* -------------------------------------------------------------------- */
	else if (psObject->nSHPType == SHPT_MULTIPOINT ||
		psObject->nSHPType == SHPT_MULTIPOINTZ ||
		psObject->nSHPType == SHPT_MULTIPOINTM)
	{
		int32 nPoints;
		int i;

		nPoints = psObject->nVertices;

		_SHPSetBounds(pabyRec + 12, psObject);

		if (bBigEndian)
			SwapWord(4, &nPoints);
		ByteCopy(&nPoints, pabyRec + 44, 4);

		for (i = 0; i < psObject->nVertices; i++)
		{
			ByteCopy(psObject->padfX + i, pabyRec + 48 + i * 16, 8);
			ByteCopy(psObject->padfY + i, pabyRec + 48 + i * 16 + 8, 8);

			if (bBigEndian)
				SwapWord(8, pabyRec + 48 + i * 16);
			if (bBigEndian)
				SwapWord(8, pabyRec + 48 + i * 16 + 8);
		}

		nRecordSize = 48 + 16 * psObject->nVertices;

		if (psObject->nSHPType == SHPT_MULTIPOINTZ)
		{
			ByteCopy(&(psObject->dfZMin), pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;

			ByteCopy(&(psObject->dfZMax), pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;

			for (i = 0; i < psObject->nVertices; i++)
			{
				ByteCopy(psObject->padfZ + i, pabyRec + nRecordSize, 8);
				if (bBigEndian)
					SwapWord(8, pabyRec + nRecordSize);
				nRecordSize += 8;
			}
		}

		if (psObject->nSHPType == SHPT_MULTIPOINTZ ||
			psObject->nSHPType == SHPT_MULTIPOINTM)
		{
			ByteCopy(&(psObject->dfMMin), pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;

			ByteCopy(&(psObject->dfMMax), pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;

			for (i = 0; i < psObject->nVertices; i++)
			{
				ByteCopy(psObject->padfM + i, pabyRec + nRecordSize, 8);
				if (bBigEndian)
					SwapWord(8, pabyRec + nRecordSize);
				nRecordSize += 8;
			}
		}
	}

	/* -------------------------------------------------------------------- */
	/*  	Write point.							*/
	/* -------------------------------------------------------------------- */
	else if (psObject->nSHPType == SHPT_POINT ||
		psObject->nSHPType == SHPT_POINTZ ||
		psObject->nSHPType == SHPT_POINTM)
	{
		ByteCopy(psObject->padfX, pabyRec + 12, 8);
		ByteCopy(psObject->padfY, pabyRec + 20, 8);

		if (bBigEndian)
			SwapWord(8, pabyRec + 12);
		if (bBigEndian)
			SwapWord(8, pabyRec + 20);

		nRecordSize = 28;

		if (psObject->nSHPType == SHPT_POINTZ)
		{
			ByteCopy(psObject->padfZ, pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;
		}

		if (psObject->nSHPType == SHPT_POINTZ ||
			psObject->nSHPType == SHPT_POINTM)
		{
			ByteCopy(psObject->padfM, pabyRec + nRecordSize, 8);
			if (bBigEndian)
				SwapWord(8, pabyRec + nRecordSize);
			nRecordSize += 8;
		}
	}

	/* -------------------------------------------------------------------- */
	/*  	Not much to do for null geometries. 							*/
	/* -------------------------------------------------------------------- */
	else if (psObject->nSHPType == SHPT_NULL)
	{
		nRecordSize = 12;
	}
	else
	{
		/* unknown type */
		assert(FALSE);
	}

	/* -------------------------------------------------------------------- */
	/*  	Establish where we are going to put this record. If we are  	*/
	/*  	rewriting and existing record, and it will fit, then put it 	*/
	/*  	back where the original came from.  Otherwise write at the end. */
	/* -------------------------------------------------------------------- */
	if (nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize - 8)
	{
		if (nShapeId == -1)
			nShapeId = psSHP->nRecords++;

		psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize;
		psSHP->panRecSize[nShapeId] = nRecordSize - 8;
		psSHP->nFileSize += nRecordSize;
	}
	else
	{
		nRecordOffset = psSHP->panRecOffset[nShapeId];
	}

	/* -------------------------------------------------------------------- */
	/*  	Set the shape type, record number, and record size. 			*/
	/* -------------------------------------------------------------------- */
	i32 = nShapeId + 1;					/* record # */
	if (!bBigEndian)
		SwapWord(4, &i32);
	ByteCopy(&i32, pabyRec, 4);

	i32 = (nRecordSize - 8) / 2;				/* record size */
	if (!bBigEndian)
		SwapWord(4, &i32);
	ByteCopy(&i32, pabyRec + 4, 4);

	i32 = psObject->nSHPType;				/* shape type */
	if (bBigEndian)
		SwapWord(4, &i32);
	ByteCopy(&i32, pabyRec + 8, 4);

	/* -------------------------------------------------------------------- */
	/*  	Write out record.   											*/
	/* -------------------------------------------------------------------- */
	if (fseek(psSHP->fpSHP, nRecordOffset, 0) != 0 || fwrite(pabyRec,
														nRecordSize, 1,
														psSHP->fpSHP) < 1)
	{
		printf("Error in fseek() or fwrite().\n\n");		free(pabyRec);
		return -1;
	}

	free(pabyRec);

	/* -------------------------------------------------------------------- */
	/*	Expand file wide bounds based on this shape.			*/
	/* -------------------------------------------------------------------- */
	if (psSHP->adBoundsMin[0] == 0.0 &&
		psSHP->adBoundsMax[0] == 0.0 &&
		psSHP->adBoundsMin[1] == 0.0 &&
		psSHP->adBoundsMax[1] == 0.0 &&
		psObject->nSHPType != SHPT_NULL)
	{
		psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
		psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
		psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0];
		psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0];
	}

	for (i = 0; i < psObject->nVertices; i++)
	{
		psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0], psObject->padfX[i]);
		psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1], psObject->padfY[i]);
		psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2], psObject->padfZ[i]);
		psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3], psObject->padfM[i]);
		psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0], psObject->padfX[i]);
		psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1], psObject->padfY[i]);
		psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2], psObject->padfZ[i]);
		psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3], psObject->padfM[i]);
	}

	return(nShapeId);
}

/************************************************************************/
/*  						SHPReadObject() 							*/
/*  																	*/
/*  	Read the vertices, parts, and other non-attribute information	*/
/*	for one shape.							*/
/************************************************************************/

SHPObject SHPAPI_CALL1(*)
SHPReadObject(SHPHandle psSHP, int hEntity)

{
SHPObject		*psShape;

/* -------------------------------------------------------------------- */
/*  	Validate the record/entity number.  							*/
/* -------------------------------------------------------------------- */
if (hEntity < 0 || hEntity >= psSHP->nRecords)
	return(NULL);

/* -------------------------------------------------------------------- */
/*  	Ensure our record buffer is large enough.   					*/
/* -------------------------------------------------------------------- */
if (psSHP->panRecSize[hEntity] + 8 > nBufSize)
{
	nBufSize = psSHP->panRecSize[hEntity] + 8;
	pabyRec = (uchar *) SfRealloc(pabyRec, nBufSize);
}

/* -------------------------------------------------------------------- */
/*  	Read the record.												*/
/* -------------------------------------------------------------------- */
fseek(psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0);
fread(pabyRec, psSHP->panRecSize[hEntity] + 8, 1, psSHP->fpSHP);

/* -------------------------------------------------------------------- */
/*	Allocate and minimally initialize the object.			*/
/* -------------------------------------------------------------------- */
	psShape = (SHPObject*) calloc(1, sizeof(SHPObject));
psShape-> nShapeId = hEntity;

memcpy(&psShape->nSHPType, pabyRec + 8, 4);
if (bBigEndian)
	SwapWord(4, &(psShape->nSHPType));

/* ==================================================================== */
/*  Extract vertices for a Polygon or Arc.				*/
/* ==================================================================== */
if (psShape->nSHPType == SHPT_POLYGON ||
	psShape->nSHPType == SHPT_ARC ||
	psShape->nSHPType == SHPT_POLYGONZ ||
	psShape->nSHPType == SHPT_POLYGONM ||
	psShape->nSHPType == SHPT_ARCZ ||
	psShape->nSHPType == SHPT_ARCM ||
	psShape->nSHPType == SHPT_MULTIPATCH)
{
	int32 nPoints, nParts;
	int i, nOffset;

	/* -------------------------------------------------------------------- */
	/*	Get the X/Y bounds.						*/
	/* -------------------------------------------------------------------- */
	memcpy(&(psShape->dfXMin), pabyRec + 8 + 4, 8);
	memcpy(&(psShape->dfYMin), pabyRec + 8 + 12, 8);
	memcpy(&(psShape->dfXMax), pabyRec + 8 + 20, 8);
	memcpy(&(psShape->dfYMax), pabyRec + 8 + 28, 8);

	if (bBigEndian)
		SwapWord(8, &(psShape->dfXMin));
	if (bBigEndian)
		SwapWord(8, &(psShape->dfYMin));
	if (bBigEndian)
		SwapWord(8, &(psShape->dfXMax));
	if (bBigEndian)
		SwapWord(8, &(psShape->dfYMax));

	/* -------------------------------------------------------------------- */
	/*  	Extract part/point count, and build vertex and part arrays  	*/
	/*  	to proper size. 												*/
	/* -------------------------------------------------------------------- */
	memcpy(&nPoints, pabyRec + 40 + 8, 4);
	memcpy(&nParts, pabyRec + 36 + 8, 4);

	if (bBigEndian)
		SwapWord(4, &nPoints);
	if (bBigEndian)
		SwapWord(4, &nParts);

	psShape->nVertices = nPoints;
	psShape->padfX = (double *) calloc(nPoints, sizeof(double));
	psShape->padfY = (double *) calloc(nPoints, sizeof(double));
	psShape->padfZ = (double *) calloc(nPoints, sizeof(double));
	psShape->padfM = (double *) calloc(nPoints, sizeof(double));

	psShape->nParts = nParts;
	psShape->panPartStart = (int *) calloc(nParts, sizeof(int));
	psShape->panPartType = (int *) calloc(nParts, sizeof(int));

	for (i = 0; i < nParts; i++)
		psShape->panPartType[i] = SHPP_RING;

	/* -------------------------------------------------------------------- */
	/*  	Copy out the part array from the record.						*/
	/* -------------------------------------------------------------------- */
	memcpy(psShape->panPartStart, pabyRec + 44 + 8, 4 * nParts);
	for (i = 0; i < nParts; i++)
	{
		if (bBigEndian)
			SwapWord(4, psShape->panPartStart + i);
	}

	nOffset = 44 + 8 + 4 * nParts;

	/* -------------------------------------------------------------------- */
	/*  	If this is a multipatch, we will also have parts types. 		*/
	/* -------------------------------------------------------------------- */
	if (psShape->nSHPType == SHPT_MULTIPATCH)
	{
		memcpy(psShape->panPartType, pabyRec + nOffset, 4 * nParts);
		for (i = 0; i < nParts; i++)
		{
			if (bBigEndian)
				SwapWord(4, psShape->panPartType + i);
		}

		nOffset += 4 * nParts;
	}

	/* -------------------------------------------------------------------- */
	/*  	Copy out the vertices from the record.  						*/
	/* -------------------------------------------------------------------- */
	for (i = 0; i < nPoints; i++)
	{
		memcpy(psShape->padfX + i, pabyRec + nOffset + i * 16, 8);

		memcpy(psShape->padfY + i, pabyRec + nOffset + i * 16 + 8, 8);

		if (bBigEndian)
			SwapWord(8, psShape->padfX + i);
		if (bBigEndian)
			SwapWord(8, psShape->padfY + i);
	}

	nOffset += 16 * nPoints;

	/* -------------------------------------------------------------------- */
	/*  	If we have a Z coordinate, collect that now.					*/
	/* -------------------------------------------------------------------- */
	if (psShape->nSHPType == SHPT_POLYGONZ ||
		psShape->nSHPType == SHPT_ARCZ ||
		psShape->nSHPType == SHPT_MULTIPATCH)
	{
		memcpy(&(psShape->dfZMin), pabyRec + nOffset, 8);
		memcpy(&(psShape->dfZMax), pabyRec + nOffset + 8, 8);

		if (bBigEndian)
			SwapWord(8, &(psShape->dfZMin));
		if (bBigEndian)
			SwapWord(8, &(psShape->dfZMax));

		for (i = 0; i < nPoints; i++)
		{
			memcpy(psShape->padfZ + i, pabyRec + nOffset + 16 + i * 8, 8);
			if (bBigEndian)
				SwapWord(8, psShape->padfZ + i);
		}

		nOffset += 16 + 8 * nPoints;
	}

	/* -------------------------------------------------------------------- */
	/*  	If we have a M measure value, then read it now.  We assume  	*/
	/*  	that the measure can be present for any shape if the size is	*/
	/*  	big enough, but really it will only occur for the Z shapes  	*/
	/*  	(options), and the M shapes.									*/
	/* -------------------------------------------------------------------- */
	if (psSHP->panRecSize[hEntity] + 8 >= nOffset + 16 + 8 * nPoints)
	{
		memcpy(&(psShape->dfMMin), pabyRec + nOffset, 8);
		memcpy(&(psShape->dfMMax), pabyRec + nOffset + 8, 8);

		if (bBigEndian)
			SwapWord(8, &(psShape->dfMMin));
		if (bBigEndian)
			SwapWord(8, &(psShape->dfMMax));

		for (i = 0; i < nPoints; i++)
		{
			memcpy(psShape->padfM + i, pabyRec + nOffset + 16 + i * 8, 8);
			if (bBigEndian)
				SwapWord(8, psShape->padfM + i);
		}
	}
}

/* ==================================================================== */
/*  Extract vertices for a MultiPoint.					*/
/* ==================================================================== */
else if (psShape->nSHPType == SHPT_MULTIPOINT ||
	psShape->nSHPType == SHPT_MULTIPOINTM ||
	psShape->nSHPType == SHPT_MULTIPOINTZ)
{
	int32 nPoints;
	int i, nOffset;

	memcpy(&nPoints, pabyRec + 44, 4);
	if (bBigEndian)
		SwapWord(4, &nPoints);

	psShape->nVertices = nPoints;
	psShape->padfX = (double *) calloc(nPoints, sizeof(double));
	psShape->padfY = (double *) calloc(nPoints, sizeof(double));
	psShape->padfZ = (double *) calloc(nPoints, sizeof(double));
	psShape->padfM = (double *) calloc(nPoints, sizeof(double));

	for (i = 0; i < nPoints; i++)
	{
		memcpy(psShape->padfX + i, pabyRec + 48 + 16 * i, 8);
		memcpy(psShape->padfY + i, pabyRec + 48 + 16 * i + 8, 8);

		if (bBigEndian)
			SwapWord(8, psShape->padfX + i);
		if (bBigEndian)
			SwapWord(8, psShape->padfY + i);
	}

	nOffset = 48 + 16 * nPoints;

	/* -------------------------------------------------------------------- */
	/*	Get the X/Y bounds.						*/
	/* -------------------------------------------------------------------- */
	memcpy(&(psShape->dfXMin), pabyRec + 8 + 4, 8);
	memcpy(&(psShape->dfYMin), pabyRec + 8 + 12, 8);
	memcpy(&(psShape->dfXMax), pabyRec + 8 + 20, 8);
	memcpy(&(psShape->dfYMax), pabyRec + 8 + 28, 8);

	if (bBigEndian)
		SwapWord(8, &(psShape->dfXMin));
	if (bBigEndian)
		SwapWord(8, &(psShape->dfYMin));
	if (bBigEndian)
		SwapWord(8, &(psShape->dfXMax));
	if (bBigEndian)
		SwapWord(8, &(psShape->dfYMax));

	/* -------------------------------------------------------------------- */
	/*  	If we have a Z coordinate, collect that now.					*/
	/* -------------------------------------------------------------------- */
	if (psShape->nSHPType == SHPT_MULTIPOINTZ)
	{
		memcpy(&(psShape->dfZMin), pabyRec + nOffset, 8);
		memcpy(&(psShape->dfZMax), pabyRec + nOffset + 8, 8);

		if (bBigEndian)
			SwapWord(8, &(psShape->dfZMin));
		if (bBigEndian)
			SwapWord(8, &(psShape->dfZMax));

		for (i = 0; i < nPoints; i++)
		{
			memcpy(psShape->padfZ + i, pabyRec + nOffset + 16 + i * 8, 8);
			if (bBigEndian)
				SwapWord(8, psShape->padfZ + i);
		}

		nOffset += 16 + 8 * nPoints;
	}

	/* -------------------------------------------------------------------- */
	/*  	If we have a M measure value, then read it now.  We assume  	*/
	/*  	that the measure can be present for any shape if the size is	*/
	/*  	big enough, but really it will only occur for the Z shapes  	*/
	/*  	(options), and the M shapes.									*/
	/* -------------------------------------------------------------------- */
	if (psSHP->panRecSize[hEntity] + 8 >= nOffset + 16 + 8 * nPoints)
	{
		memcpy(&(psShape->dfMMin), pabyRec + nOffset, 8);
		memcpy(&(psShape->dfMMax), pabyRec + nOffset + 8, 8);

		if (bBigEndian)
			SwapWord(8, &(psShape->dfMMin));
		if (bBigEndian)
			SwapWord(8, &(psShape->dfMMax));

		for (i = 0; i < nPoints; i++)
		{
			memcpy(psShape->padfM + i, pabyRec + nOffset + 16 + i * 8, 8);
			if (bBigEndian)
				SwapWord(8, psShape->padfM + i);
		}
	}
}

/* ==================================================================== */
/*  	Extract vertices for a point.   								*/
/* ==================================================================== */
else if (psShape->nSHPType == SHPT_POINT ||
	psShape->nSHPType == SHPT_POINTM ||
	psShape->nSHPType == SHPT_POINTZ)
{
	int nOffset;

	psShape->nVertices = 1;
	psShape->padfX = (double *) calloc(1, sizeof(double));
	psShape->padfY = (double *) calloc(1, sizeof(double));
	psShape->padfZ = (double *) calloc(1, sizeof(double));
	psShape->padfM = (double *) calloc(1, sizeof(double));

	memcpy(psShape->padfX, pabyRec + 12, 8);
	memcpy(psShape->padfY, pabyRec + 20, 8);

	if (bBigEndian)
		SwapWord(8, psShape->padfX);
	if (bBigEndian)
		SwapWord(8, psShape->padfY);

	nOffset = 20 + 8;

	/* -------------------------------------------------------------------- */
	/*  	If we have a Z coordinate, collect that now.					*/
	/* -------------------------------------------------------------------- */
	if (psShape->nSHPType == SHPT_POINTZ)
	{
		memcpy(psShape->padfZ, pabyRec + nOffset, 8);

		if (bBigEndian)
			SwapWord(8, psShape->padfZ);

		nOffset += 8;
	}

	/* -------------------------------------------------------------------- */
	/*  	If we have a M measure value, then read it now.  We assume  	*/
	/*  	that the measure can be present for any shape if the size is	*/
	/*  	big enough, but really it will only occur for the Z shapes  	*/
	/*  	(options), and the M shapes.									*/
	/* -------------------------------------------------------------------- */
	if (psSHP->panRecSize[hEntity] + 8 >= nOffset + 8)
	{
		memcpy(psShape->padfM, pabyRec + nOffset, 8);

		if (bBigEndian)
			SwapWord(8, psShape->padfM);
	}

	/* -------------------------------------------------------------------- */
	/*  	Since no extents are supplied in the record, we will apply  	*/
	/*  	them from the single vertex.									*/
	/* -------------------------------------------------------------------- */
	psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
	psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
	psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
	psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
}

return(psShape);
}

/************************************************************************/
/*  						  SHPTypeName() 							*/
/************************************************************************/

const char SHPAPI_CALL1(*)
SHPTypeName(int nSHPType)

{
switch( nSHPType )
{
case SHPT_NULL:
return "NullShape";

case SHPT_POINT : return "Point";

case SHPT_ARC : return "Arc";

case SHPT_POLYGON : return "Polygon";

case SHPT_MULTIPOINT : return "MultiPoint";

case SHPT_POINTZ : return "PointZ";

case SHPT_ARCZ : return "ArcZ";

case SHPT_POLYGONZ : return "PolygonZ";

case SHPT_MULTIPOINTZ : return "MultiPointZ";

case SHPT_POINTM : return "PointM";

case SHPT_ARCM : return "ArcM";

case SHPT_POLYGONM : return "PolygonM";

case SHPT_MULTIPOINTM : return "MultiPointM";

case SHPT_MULTIPATCH : return "MultiPatch";

	  default : return "UnknownShapeType";
}
}

/************************************************************************/
/*  						SHPPartTypeName()   						*/
/************************************************************************/

const char SHPAPI_CALL1(*)
SHPPartTypeName(int nPartType)

{
switch( nPartType )
{
case SHPP_TRISTRIP:
return "TriangleStrip";

case SHPP_TRIFAN : return "TriangleFan";

case SHPP_OUTERRING : return "OuterRing";

case SHPP_INNERRING : return "InnerRing";

case SHPP_FIRSTRING : return "FirstRing";

case SHPP_RING : return "Ring";

	  default : return "UnknownPartType";
}
}

/************************************************************************/
/*  						SHPDestroyObject()  						*/
/************************************************************************/

void SHPAPI_CALL SHPDestroyObject(SHPObject* psShape)
{
	if (psShape == NULL)
		return;

	if (psShape->padfX != NULL)
		free(psShape->padfX);
	if (psShape->padfY != NULL)
		free(psShape->padfY);
	if (psShape->padfZ != NULL)
		free(psShape->padfZ);
	if (psShape->padfM != NULL)
		free(psShape->padfM);

	if (psShape->panPartStart != NULL)
		free(psShape->panPartStart);
	if (psShape->panPartType != NULL)
		free(psShape->panPartType);

	free(psShape);
}
